<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import { useDebounceFn } from '@vueuse/core'
import { ref, unref, watch } from 'vue'

defineOptions({ name: 'ERemoteSelect' })

// 下拉数据结构
interface InfoType {
  [key: string]: string | number
}

// 更新父组件的value
const emit = defineEmits(['update:modelValue', 'change'])
const pageNo = ref(1)
const searchValue = ref('')
const props = defineProps({
  // 类型 user: 用户, dept: 部门
  type: propTypes.string.def('user'),
  // 默认的placeholder
  placeholder: propTypes.string.def('请输入'),
  // valueModel: 用于v-model双向绑定的值,类型为字符串,默认为空字符串
  modelValue: {
    // Vue 3中默认是modelValue
    type: [String, Number, Object],
    default: ''
  },
  // idKey: 用于标识数据项唯一性的字段,默认为id
  idKey: propTypes.string.def('id'),
  // 展示的字段及顺序
  showFiled: propTypes.array.def(['userNum', 'username', 'nameZh', 'nameEn']),
  // 下拉数据查询接口
  queryApi: propTypes.func.def(() => { }),

  // 获取value
  fetchValueFunc :  propTypes.func.def(() => { }),

  // 是否过滤自建数据
  needFilter: propTypes.bool.def(true),
  // 接口过滤方法
  filterApi: propTypes.func.def((result: any[]) => {
    return result?.filter((item) => item.source !== '自建')
  }),
  // 是否搜索框条件：搜索框不用在watch中重新查询
  isSearch: propTypes.bool.def(false),
  // 是否可清空
  clearable: propTypes.bool.def(true),
  
  // 是否显示加载更多
  isLoadMore: propTypes.bool.def(false),
  // 每页条数
  limit: propTypes.number.def(10)
})

// 选项
const options = ref<InfoType[]>([])

// 是否有搜索内容
const hasSearchInput = ref(false)

// 控件绑定值
const value = ref<number | string>(props.modelValue as number)

// 选中信息
const info = ref<InfoType>({})

// 选择选项
const handleChange = (val: string) => {
  const data = options.value.find((item) => item[props.idKey] === val)
  if (data) {
    info.value = data
    // 更新父组件的value
    emit('update:modelValue', info.value)

    // 选中后重置搜索状态
    hasSearchInput.value = false
    options.value = []
  } else {
    emit('update:modelValue', {})
  }
  emit('change', val)
}

// 根据 showFiled 生成对应 label
const getLabel = (item: InfoType) => {
  return (props?.showFiled ?? []).map((field: string) => item[field])
    .filter((item) => item)
    .join('-')
}

// 远程搜索
const loading = ref<boolean>(false)
const remoteMethod = useDebounceFn(async (query: string) => {
  console.log('query=>', props.queryApi)
  // 用户类型，输入非汉字字符时，小于3个字符不查询，防止系统崩溃
  if (props.type == 'user') {
    // 检查是否包含汉字
    const hasChinese = /[\u4e00-\u9fa5]/.test(query)
    // 如果不包含汉字且长度小于3，则不查询
    if (!hasChinese && query.length < 3) {
      hasSearchInput.value = false
      options.value = []
      return
    }
  }
  hasSearchInput.value = Boolean(query && query.trim() !== '')
  if (!hasSearchInput.value) {
    options.value = []
    return
  }
  loading.value = true
  try {
    if (typeof props.queryApi === 'function') {
      let result: InfoType[] = []
      result = await props.queryApi({ condition: query })
      if (props.type == 'user') {
        result = result.filter((item) => item.empStatus)
      }
      if (typeof props.filterApi === 'function' && props.needFilter && result) {
        result = props.filterApi(result)
      }
      options.value = result || []
      searchValue.value = query
    }
  } catch (error) {
    console.error('远程搜索出错:', error)
    options.value = []
  } finally {
    loading.value = false
  }
}, 200)

// 加载更多
const handleLoadMore = async () => {
  console.log('handleLoadMore')
  if (typeof props.queryApi === 'function') {
    pageNo.value++
    const result = await props.queryApi({ condition: unref(searchValue), pageNo: pageNo.value, pageSize: props.limit })
    options.value = [...options.value, ...result]
  }
}



// 监听modelValue变化, 详情页查询回填数据，不需要触发update:valueModel
watch(
  () => props.modelValue,
  async (newVal) => {
    if (props.isSearch) {
      value.value = newVal as string
      return
    }
    if (newVal) {
      // 只需判断是否有值即可
      loading.value = true
      try {
        if (typeof props.fetchValueApi === 'function') {
          let result: InfoType[] = []
          if (props.type === 'user' && !isNaN(Number(newVal))) {
            result = [await props.fetchValueApi(newVal)].filter((item) => item)
          } else if (props.type === 'dept' && !isNaN(Number(newVal))) {
            result = [await props.fetchValueApi(newVal)].filter((item) => item)
          } else {
            result = await props.fetchValueApi({ condition: newVal })
            if (!Array.isArray(result)) {
              result = [result].filter((item) => item)
            }
          }
          if (result.length > 0) {
            options.value = result
            const firstItem = result[0]
            value.value = firstItem[props.idKey]
          } else {
            value.value = newVal as number
          }
        }
      } catch (error) {
        value.value = newVal as number
        console.error('获取下拉数据失败:', error)
      } finally {
        loading.value = false
      }
    } else {
      value.value = ''
    }
  },
  {
    immediate: true
  }
)
// 暴露方法
defineExpose({
  // 获取用户信息
  getValue: () => info.value,
  // 清空value
  clearValue: () => {
    value.value = ''
    info.value = {}
  }
})
</script>

<template>
  <el-select :model-value="modelValue" remote :clearable="props.clearable" filterable collapse-tags reserve-keyword
    :popper-class="`custom-select-dropdown ${hasSearchInput ? 'show-dropdown' : ''}`" :remote-method="remoteMethod"
    :loading="loading" :placeholder="placeholder" @change="handleChange" v-bind="$attrs">
    <el-option v-for="item in options" :key="item[props.idKey]" :label="getLabel(item)" :value="item[props.idKey]" />

    <template #footer v-if="isLoadMore">
      <div class="text-center">
        <e-button type="primary" link @click="handleLoadMore">加载更多</e-button>
      </div>
    </template>
  </el-select>
</template>

<style>
/* 默认隐藏下拉菜单 */
.custom-select-dropdown {
  display: none !important;
}

/* 有搜索内容时才显示下拉菜单 */
.custom-select-dropdown.show-dropdown {
  display: block !important;
}
</style>
